package com.cskaoyan.javse.object._6clone;

/**
 * @description: clone()方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * clone方法
 * 1.方法的声明:
 *       protected native Object clone() throws CloneNotSupportedException;
 *          a.首先注意访问权限,它的访问权限是protected,在不同包下只能在子类当中创建子类自身对象才能够访问
 *       这意味克隆方法,只能自己克隆自己,这在多数情况下没有意义,需要在子类中重写方法的访问权限
 *          b.它是一个本地native方法,它的实现不需要关心
 *          c.方法的返回值类型是Object,在子类中可以重写该返回值类型
 *          d.throws CloneNotSupportedException 这里仍然是异常抛出的声明
 *
 * 2.方法的作用:
 *      克隆,生物学意义上的克隆,是用生物的体细胞,经过无性繁殖,得到相同基因型后代的过程
 *      java中的克隆clone()有类似的作用,它是得到一个和调用该方法的对象的成员变量取值完全一致的对象
 *      所以克隆也是一种创建对象的方式,它会得到一个和原先对象属性值完全一致,但又相互独立的对象
 *      克隆:
 *          1.得到完全独立的一个对象
 *          2.属性值和原先的对象一样
 *
 * 3.使用clone方法的步骤:
 *      a.要解决访问权限问题,需要在子类中重写clone方法的访问权限
 *      b.(选做,非必须)重写方法的返回值类型为自身类型
 *      c.一个类想要做克隆操作,必须要先实现一个接口java.lang.Cloneable,表示该类允许进行克隆
 *          如果该类没有实现接口,又要克隆操作,就会抛出异常:CloneNotSupportedException
 *      d.以上步骤完成,就可以克隆一个对象了
 *
 * 以上,就完成了克隆最最基本的操作,接下来讲一些细节问题:
 *
 * 1.Cloneable接口的问题
 *      Cloneable接口是一个空接口,让类去实现一个空接口,有什么意义?
 *      实现空接口虽然没有得到任何成员,但仍然改变了这个类的数据类型
 *      --> 让这个类从原先不是这个接口的实现类,变成了实现类
 *      --> 一旦成为接口的实现类,就可以用instanceof关键字进行类型的判断
 *      --> 这样就可以完成一系列的操作了
 *      Cloneable就是这样的一个工作原理,像Cloneable这种没有任何成员,一个空接口
 *      它起着一个标记的作用,称之为"标记接口"
 *      被Cloneable标记的类是允许做克隆操作的,反之不允许
 *
 * 2.clone方法具体实现的重写,在正常情况下,我们使用Object默认的本地方法实现就足够了,不需要重写实现,如有必要API文档
 *      中也提供了一些规则:
 *      对于任何对象 x，表达式：
 * ​		1，x.clone() != x 为 true（这一条是必须的）
 * ​		2，x.clone().getClass() == x.getClass() 为true(克隆后不能改变类型,在正常情况下这一条需要满足)
 * ​		3，x.clone().equals(x) 为true（一般情况下为true，但这并不是必须要满足的要求,只有在按照成员变量取值重写equals方法后才会满足）
 *
 * 3.clone方法是一种创建对象的方式,和new对象的方式是平行的关系,是独立的关系
 *      clone不依赖于构造器创建对象
 *      创建对象的方式:
 *          a.new
 *          b.clone
 *          c.反射
 *          d.序列化反序列化
 *
 * 4.如果类中有引用数据类型的成员变量,那么clone方法的使用就要格外注意
 *      java当中Object当中clone方法默认实现是,完全直接拷贝一份成员变量
 *      而对于引用数据类型而言,拷贝的是引用 --> 克隆后的引用和原先的引用指向同一个对象
 *      这样的话,使用任何一个引用去修改对象的状态,都会互相影响，这样两个对象就不是完全独立
 *      Object类当中的clone方法的实现，我们称之为“浅克隆”
 *      对应的，如果能够让引用数据类型成员变量之间也能相互独立，称之为“深度克隆”
 *
 *  5.深度克隆：
 *      如何让克隆后的引用和原先的引用独立呢？
 *          a.将引用指向的对象，再克隆一份
 *          b.然后让克隆后的引用指向它
 *      于是两个对象就完全独立了,就完成了深度克隆
 *
 */
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        /*Student s = new Student(18, 100);
        Student s2 = new Student();
        //克隆Student对象,得到一个和原先对象一模一样的对象
        //'clone()' has protected access in 'java.lang.Object'
        //Unhandled exception: java.lang.CloneNotSupportedException
        Student cloneStu = s.clone();
        Student cloneStu2 = s2.clone();
        System.out.println(cloneStu == s);
        System.out.println(s);
        System.out.println(cloneStu);

        System.out.println(s.equals(cloneStu));*/

        Student s = new Student(18, 750, new Dog(1000));
        Student cloneStu = s.clone();
        s.d.price = 2000;
        System.out.println(cloneStu.d.price);

        System.out.println(s.d);
        System.out.println(cloneStu.d);

    }
}

class Dog implements Cloneable{
    double price;

    public Dog() {
    }

    public Dog(double price) {
        this.price = price;
    }

    @Override
    protected Dog clone() throws CloneNotSupportedException {
        return ((Dog) super.clone());
    }
}

class Student implements Cloneable {
    int age;
    double score;
    Dog d;


   /* {
        System.out.println("构造器执行了~");
    }*/

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    public Student(int age, double score, Dog d) {
        this.age = age;
        this.score = score;
        this.d = d;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    //重写clone方法的访问权限
    @Override
    protected Student clone() throws CloneNotSupportedException {
        //仍然选择调用父类默认实现
        //深度克隆的步骤
        //1.深度克隆是在浅克隆基础上玩的
        Student cloneStu = (Student) super.clone();
        //2.需要把Dog对象克隆一份
        Dog cloneDog = cloneStu.d.clone();
        //3.将拷贝引用指向拷贝对象
        cloneStu.d = cloneDog;
        return cloneStu;
        //return ((Student) super.clone());
    }

}

//interface CloneableSon  extends Cloneable{}